#include <memory.h>
#include <memory>
#include <map>
#include <set>

#include "../robot.h"

#include "gamer.hpp"
#include "supergamer.hpp"

using namespace robot;
using namespace Client;
using namespace Proto;

class BadField {};

class RobotAdapter : public Robot
{
	std::auto_ptr<AI> impl_;
	
public:
	RobotAdapter(AI *impl) :
		impl_(impl)
	{}

	Action Move(const Environment &env)		
	{
		if (env.width != FIELD_COLS || env.height != FIELD_ROWS)	
			throw BadField();
			
		unsigned char field[FIELD_COLS*FIELD_ROWS];
		Player player;
		
		
		// setup player
		player.x = static_cast<short>(env.x);
		player.y = static_cast<short>(env.y);
		player.velx = env.vx;
		player.vely = env.vy;
		player.energy = env.energy;
		player.maxEnergy = env.energy_max;
		// WHERE IS THE MAXIMAL VELOCITY?
		player.maxVelocity = nspeeds - 1;
		player.maxFire = env.fire_damage_max;
		player.durability = env.health;
		player.shootCooldown = env.turns_before_shot + 1;
		// hack hack hack
		player.shootSector = 1024;
		// WHERE IS THE FOLLOWING?
		// short visualSector, shootSector;
		// bool isAlive;

		// setup field
		memset(field, FIELD_EMPTY, sizeof(field));
		for (std::map<Point, BasicSquare>::const_iterator i = env.terrain.begin(); i != env.terrain.end(); ++i) {
			unsigned char objectId = FIELD_EMPTY;
			
			// awful interface :(
			// why could i not determine true object type?
			// it has made me to write this hack, dirty hack :(
			if (i->second.GetEnergyContribution() > 0)
				objectId = FIELD_CHARGE_STATION;
			else if (i->second.IsKilling())
				objectId = FIELD_RIVER;
			else if (i->second.GetMaxSpeed() == 4)
				objectId = FIELD_SWAMP;
			else if (!i->second.IsEnterable())
				objectId = FIELD_FOREST;
			else
				objectId = FIELD_BRIDGE;
			field[i->first.x + i->first.y*FIELD_COLS] = objectId;
		}
		
		for (unsigned i = 0; i < env.robots.size(); ++i) {
			int x = static_cast<int>(env.robots[i].x);
			int y = static_cast<int>(env.robots[i].y);
			
			field[x + y*FIELD_COLS] = FIELD_ENEMY;
		}

		for (unsigned i = 0; i < env.treasures.size(); ++i) {
			field[env.treasures[i].x + env.treasures[i].y*FIELD_COLS] = FIELD_BONUS;
		}
		

		impl_->load(field, player);
	
		Client2Server::SpeedAdjust sa = {0, 0, 0};
		Client2Server::Fire fire = {0, 0, 0};
		Action act;
		
		impl_->command(sa, fire);
		act.stop = sa.isBraking;
		act.ax = sa.xAdjust;
		act.ay = sa.yAdjust;
		act.fire = fire.power > 0;
		act.fire_x = fire.xCoord;
		act.fire_y = fire.yCoord;
		act.fire_damage = fire.power;
		
		return act;
	}
};

extern "C" {
	Robot* getRobot() {
		return new RobotAdapter(new SuperGamer);
	}
}
